package com.ujson.base.domain.repository.impl;

import com.ujson.base.domain.entity.Aggregate;
import com.ujson.base.domain.entity.EntityDiff;
import com.ujson.base.domain.entity.Identifier;
import com.ujson.base.domain.entity.Repository;
import com.ujson.base.domain.repository.AggregateManager;
import lombok.AccessLevel;
import lombok.Getter;

import javax.validation.constraints.NotNull;
import java.util.Objects;

public abstract class RepositorySupport<T extends Aggregate<ID>, ID extends Identifier> implements Repository<T, ID> {

    @Getter
    private Class<T> targetClass;

    @Getter(AccessLevel.PROTECTED)
    private AggregateManager<T, ID> aggregateManager;

    public RepositorySupport(Class<T> targetClass) {
        this.targetClass = targetClass;
        aggregateManager = AggregateManagerFactory.newInstance(targetClass);
    }

    protected abstract void onInsert(T aggregate);
    protected abstract T onSelect(ID id);
    protected abstract void onUpdate(T aggregate, EntityDiff diff);
    protected abstract void onDelete(T aggregate);

    @Override
    public void attach(@NotNull T aggregate) {
        this.aggregateManager.attach(aggregate);
    }

    @Override
    public void detach(@NotNull T aggregae) {
        this.aggregateManager.detach(aggregae);
    }

    @Override
    public T find(@NotNull ID id) {
        T aggregate = this.onSelect(id);
        if (Objects.nonNull(aggregate)) {
            this.attach(aggregate);
        }
        return aggregate;
    }

    @Override
    public void remove(@NotNull T aggregate) {
        this.onDelete(aggregate);

        this.detach(aggregate);
    }

    @Override
    public void save(@NotNull T aggregate) {
        if (Objects.isNull(aggregate.getId())) {
            this.onInsert(aggregate);
            this.attach(aggregate);
            return ;
        }

        EntityDiff diff = null;
        try {
            diff = aggregateManager.detectChanges(aggregate);
        } catch (IllegalAccessException e) {
            return ;
        }
        if (diff.isEmpty()) {
            return ;
        }

        this.onUpdate(aggregate, diff);

        // 将db的带来的变化
        aggregateManager.merge(aggregate);
    }
}
